package com.wei.czz.common.utils;

import com.wei.czz.common.constant.RegexConstant;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.exception.CzzException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Random;
import java.util.regex.Pattern;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2021-05-31 16:19:00
 * className: CaesarUtils 凯撒加解密工具类
 * version: 1.0
 * description:
 *  加密算法为：凯撒加密优化版
 */
public class CaesarUtils {

    private static final Logger log = LoggerFactory.getLogger(CaesarUtils.class);

    /**
     * 字符移动位数数组
     */
    private static final int[] moveArr = { 12,32,5,21,29,30,2,15,7,16,32,6,20,17,13,25,27,33,19,14,24,9,
            28,18,16,34,3,19,12,23,4,34,36,10,20,11,8 };
    /**
     * 校验密文是否格式正确正则（匹配规则：数字组合，逗号分割）
     */
    private static final String REGEX = "^(\\d+,)+(\\d+)$";

    /**
     * 凯撒加密优化版本五（字符串加密）
     * @param plainText 待加密明文
     * @return 密文
     */
    public static String encryption(String plainText) {
        int length = plainText.length();
        // 数组存储的明文字符和干扰字符的总数
        int[] res = new int[mathPowerValue(2 * length)];
        // 结果数组的存值下标
        int count = 0;
        // 随机数生成器
        Random rand = new Random();
        // 移位数值取值开始下标
        int index = rand.nextInt(moveArr.length);
        // 记录取值开始下标
        int startIndex = index;
        int c;
        for (int i = 0; i < length; i++) {
            c = plainText.charAt(i);
            // 字符向右移动，移动位数从arr数组中取值
            c += moveArr[index++];
            if (c > 126) {
                // 如果移动后的字符大于ascii码表的126号字符
                c -= 94;
            }
            // 存储加密后的字符和干扰字符
            res[count++] = c;
            res[count++] = rand.nextInt(94) + 33;

            index %= moveArr.length;
        }
        // 设置移位数组的取值开始下标
        res[1] = startIndex + 33;
        // 设置明文字符串的长度
        res[3] = length + res[1];
        // 补足剩余位置的字符
        length = res.length;
        for (; count < length; count++) {
            res[count] = rand.nextInt(94) + 33;
        }
        StringBuilder sb = new StringBuilder();
        for (int num : res) {
            sb.append(num).append(',');
        }
        return sb.substring(0, sb.length() - 1);
    }

    /**
     * 凯撒加密优化版本五（密码字符串解密）
     * @param pwd 密文字符串
     * @return 原始密码
     */
    public static String decodePwd(String pwd) {
        if (!Pattern.matches(REGEX, pwd)) {
            log.info("用户密码格式不符合条件");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "密码密文不符合规则");
        }

        // 分割密文字符串，获取密文数组
        String[] pwdArray = pwd.split(",");
        // 密文长度
        int length = pwdArray.length;
        // 判断密文长度是不是2的n次幂数值
        if (length != mathPowerValue(length)) {
            log.info("用户密码长度不符合条件");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "密码密文长度不符合条件");
        }
        int c = Integer.parseInt(pwdArray[1]);
        // 获取密文字符串存储的移位数组取值开始下标
        int index = c - 33;
        // 获取密文中存储的实际明文长度
        length = Integer.parseInt(pwdArray[3]) - c;
        StringBuilder sb = new StringBuilder(length);

        length *= 2;
        for (int i = 0; i < length; i += 2) {
            c = Integer.parseInt(pwdArray[i]);
            // 字符向左移动，移动位数从arr数组中取值
            c -= moveArr[index++];
            if (c < 33) {
                // 如果移动后的字符小于ascii码表的33号字符
                c += 94;
            }
            sb.append((char) c);

            index %= moveArr.length;
        }
        String password = sb.toString();
        if (!Pattern.matches(RegexConstant.PWD_REGEX, password)) {
            log.info("用户密码格式错误");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "您的新密码格式错误，请确认后重新输入");
        }
        return password;
    }

    /**
     * 计算得到第一个大于等于cap值的2的n次幂数值
     * @param cap 基准值
     * @return 2的n次幂数值
     */
    private static int mathPowerValue(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : n + 1;
    }

}
